package santagen;

import java.io.PrintWriter;
import static java.lang.Math.PI;
import static java.lang.Math.abs;
import static java.lang.Math.sin;
import org.jenetics.Chromosome;
import org.jenetics.EnumGene;
import org.jenetics.GeneticAlgorithm;
import org.jenetics.Genotype;
import org.jenetics.NumberStatistics.Calculator;
import org.jenetics.Optimize;
import org.jenetics.PartiallyMatchedCrossover;
import org.jenetics.PermutationChromosome;
import org.jenetics.SwapMutator;
import org.jenetics.util.Factory;
import org.jenetics.util.Function;
import org.jscience.mathematics.number.Float64;
import rjp.map.ArraySantaMap;

class FF implements Function<Genotype<EnumGene<Integer>>, Float64> {

    private final ArraySantaMap _map;

    public FF(final ArraySantaMap map) {
        _map = map;
    }

    @Override
    public Float64 apply(Genotype<EnumGene<Integer>> genotype) {
        Chromosome<EnumGene<Integer>> path1 =
                genotype.getChromosome(0);

        Chromosome<EnumGene<Integer>> path2 =
                genotype.getChromosome(1);

        double length1 = 0.0;
        int[] p1 = new int[path1.length()];
        for (int i = 0, n = path1.length(); i < n; ++i) {
            final int from = path1.getGene(i).getAllele();
            final int to = path1.getGene((i + 1) % n).getAllele();
            p1[from] = to;
            length1 += _map.getDistance(from, to);
        }

        double length2 = 0.0;
        for (int i = 0, n = path2.length(); i < n; ++i) {
            final int from = path2.getGene(i).getAllele();
            final int to = path2.getGene((i + 1) % n).getAllele();
            if (p1[from] == to || p1[to] == from) {
                return Float64.valueOf(Double.MAX_VALUE);
            }
            length2 += _map.getDistance(from, to);
        }
        return Float64.valueOf(Math.max(length1, length2));
    }
}

public class SantaGen {

    public static void main(String[] args) throws Exception {


        int popSize = Integer.parseInt(args[2]);
        int generations = Integer.parseInt(args[3]);
        int evolveSize = Integer.parseInt(args[4]);
        
        ArraySantaMap map = new ArraySantaMap(args[0]);

        Function<Genotype<EnumGene<Integer>>, Float64> ff =
                new FF(map);
        Factory<Genotype<EnumGene<Integer>>> gt = Genotype.valueOf(
                PermutationChromosome.ofInteger(map.size()), PermutationChromosome.ofInteger(map.size()));
        final GeneticAlgorithm<EnumGene<Integer>, Float64> ga = new GeneticAlgorithm<>(gt, ff, Optimize.MINIMUM);
        ga.setStatisticsCalculator(
                new Calculator<EnumGene<Integer>, Float64>());
        ga.setPopulationSize(1000);
//        ga.setSelectors(new TruncationSelector<EnumGene<Integer>, Float64>());
        ga.setAlterers(
                new SwapMutator<EnumGene<Integer>>(0.4),
                new PartiallyMatchedCrossover<Integer>(0.6));

        ga.setup();

        
        double bestScore = Double.MAX_VALUE;
        PrintWriter out = new PrintWriter(args[1]);
        for (int i = 0; i < generations; i++) {
            ga.evolve(evolveSize);
            double score = ga.getBestStatistics().getBestFitness().doubleValue();
            if (score < bestScore) {
                bestScore = score;
                out.println(score);
                System.out.println(ga.getBestStatistics());
                System.out.println(ga.getBestPhenotype());
                Chromosome<EnumGene<Integer>> path1 = ga.getBestPhenotype().getGenotype().getChromosome(0);
                Chromosome<EnumGene<Integer>> path2 = ga.getBestPhenotype().getGenotype().getChromosome(1);
                StringBuilder sb = new StringBuilder();
                for (int j = 0, n = path1.length(); j < n; j++) {
                    int index = path1.getGene(j).getAllele();
                    sb.append(index).append(" ");
                }
                
                out.println(sb.toString().trim());
                sb = new StringBuilder();
                for (int j = 0, n = path1.length(); j < n; j++) {
                    int index = path2.getGene(j).getAllele();
                    sb.append(index).append(" ");
                }
                out.println(sb.toString().trim());
                out.println();
                out.flush();
            }
        }
        out.close();
        out.close();

    }

    private static double[][] adjacencyMatrix(int stops) {
        double[][] matrix = new double[stops][stops];
        for (int i = 0; i < stops; ++i) {
            for (int j = 0; j < stops; ++j) {
                matrix[i][j] = chord(stops, abs(i - j), RADIUS);
            }
        }
        return matrix;
    }

    private static double chord(int stops, int i, double r) {
        return 2.0 * r * abs(sin((PI * i) / stops));
    }
    private static double RADIUS = 10.0;

}
